package org.cxy.job;

import com.xxl.job.core.context.XxlJobHelper;
import com.xxl.job.core.handler.annotation.XxlJob;
import lombok.extern.slf4j.Slf4j;
import org.cxy.entity.TargetTable;
import org.cxy.model.IdRange;
import org.cxy.model.Segment;
import org.cxy.model.SegmentConfig;
import org.cxy.service.TargetTableService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import java.util.ArrayList;
import java.util.List;

@Component
@Slf4j
public class FixedStepSegmentTaskHandler {
    
    @Autowired
    private TargetTableService targetTableService;
    
    /**
     * 固定步长分段任务 - MyBatis-Plus 版本
     */
    @XxlJob("fixedStepSegmentTask")
    public void fixedStepSegmentTask() {
        int shardIndex = XxlJobHelper.getShardIndex();
        int shardTotal = XxlJobHelper.getShardTotal();
        
        log.info("开始执行固定步长分段任务(MyBatis-Plus), 分片[{}/{}]", shardIndex + 1, shardTotal);
        long startTime = System.currentTimeMillis();
        
        try {
            // 1. 查询主键ID范围
            IdRange idRange = targetTableService.getIdRange();
            if (idRange == null || idRange.getMinId() == null) {
                log.warn("未找到待处理数据");
                XxlJobHelper.handleSuccess("无待处理数据");
                return;
            }
            
            // 2. 计算分段参数
            SegmentConfig segmentConfig = calculateSegmentConfig(idRange);
            log.info("主键范围: {}-{}, 总记录数: {}, 分段大小: {}, 总段数: {}", idRange.getMinId(), idRange.getMaxId(), idRange.getTotalRecords(), segmentConfig.getSegmentSize(), segmentConfig.getTotalSegments());
            
            // 3. 分配分段给当前分片
            List<Segment> mySegments = assignSegmentsToShard(segmentConfig, shardIndex, shardTotal);
            log.info("分片[{}]分配到 {} 个分段", shardIndex, mySegments.size());
            
            if (mySegments.isEmpty()) {
                log.info("分片[{}]无分段需要处理", shardIndex);
                XxlJobHelper.handleSuccess("无分段需要处理");
                return;
            }
            
            // 4. 处理分配的分段
            int totalProcessed = processAssignedSegments(mySegments);
            
            long cost = System.currentTimeMillis() - startTime;
            log.info("分片[{}]处理完成, 共处理 {} 条记录, 耗时: {}ms", 
                    shardIndex, totalProcessed, cost);
            
            XxlJobHelper.handleSuccess("处理完成, 共处理 " + totalProcessed + " 条记录");
            
        } catch (Exception e) {
            log.error("分片[{}]执行失败", shardIndex, e);
            XxlJobHelper.handleFail("执行失败: " + e.getMessage());
        }
    }
    
    /**
     * 计算分段配置
     */
    private SegmentConfig calculateSegmentConfig(IdRange idRange) {
        long totalRecords = idRange.getTotalRecords();
        
        // 动态计算分段大小
        long segmentSize;
        if (totalRecords < 10000) {
            segmentSize = 100;
        } else if (totalRecords < 100000) {
            segmentSize = 500;
        } else {
            segmentSize = 1000;
        }
        
        SegmentConfig config = new SegmentConfig();
        config.setMinId(idRange.getMinId());
        config.setMaxId(idRange.getMaxId());
        config.setSegmentSize(segmentSize);
        config.setTotalSegments((int) Math.ceil((double) totalRecords / segmentSize));
        
        return config;
    }


    /**
     * 分配分段给当前分片
     * 将总分段均匀分配给各个分片，确保负载均衡
     */
    private List<Segment> assignSegmentsToShard(SegmentConfig config, int shardIndex, int shardTotal) {
        List<Segment> segments = new ArrayList<>();

        // 计算分配参数
        int segmentsPerShard = (int) Math.ceil((double) config.getTotalSegments() / shardTotal);
        int startSegment = shardIndex * segmentsPerShard;
        int endSegment = Math.min(startSegment + segmentsPerShard - 1, config.getTotalSegments() - 1);

        // 记录分配情况
        logAssignmentInfo(config, shardIndex, shardTotal, segmentsPerShard, startSegment, endSegment);

        // 生成分段
        for (int segmentIndex = startSegment; segmentIndex <= endSegment; segmentIndex++) {
            long segmentStart = config.getMinId() + segmentIndex * config.getSegmentSize();
            long segmentEnd = Math.min(segmentStart + config.getSegmentSize() - 1, config.getMaxId());
            segments.add(new Segment(segmentIndex, segmentStart, segmentEnd));
        }

        return segments;
    }

    /**
     * 记录分段分配情况
     */
    private void logAssignmentInfo(SegmentConfig config, int shardIndex, int shardTotal,
                                   int segmentsPerShard, int startSegment, int endSegment) {
        log.info("分片分配: 分片[{}] 分段[{}-{}] 共{}段 (总分段:{}, 总分片:{})",
                shardIndex, startSegment, endSegment,
                (endSegment - startSegment + 1), config.getTotalSegments(), shardTotal);
    }
    
    /**
     * 处理分配的分段
     */
    private int processAssignedSegments(List<Segment> segments) {
        int totalProcessed = 0;
        
        for (Segment segment : segments) {
            log.info("处理分段 [{}-{}]", segment.getStartId(), segment.getEndId());
            int processedInSegment = processSegment(segment);
            totalProcessed += processedInSegment;
            
            log.info("分段 [{}-{}] 处理完成, 本分段处理 {} 条记录", 
                    segment.getStartId(), segment.getEndId(), processedInSegment);
            
            // 短暂休息
            try {
                Thread.sleep(10);
            } catch (InterruptedException e) {
                Thread.currentThread().interrupt();
                break;
            }
        }
        
        return totalProcessed;
    }
    
    /**
     * 处理单个分段
     */
    private int processSegment(Segment segment) {
        try {
            // 使用 MyBatis-Plus 查询分段数据
            List<TargetTable> records = targetTableService.getSegmentData(
                segment.getStartId(), segment.getEndId());
            
            if (records.isEmpty()) {
                return 0;
            }
            
            // 批量处理记录
            int successCount = 0;
            List<Long> successIds = new ArrayList<>();
            
            for (TargetTable record : records) {
                if (processSingleRecord(record)) {
                    successCount++;
                    successIds.add(record.getId());
                }
            }
            
            // 批量更新成功记录状态
            if (!successIds.isEmpty()) {
                targetTableService.batchUpdateToProcessed(successIds);
            }
            
            return successCount;
            
        } catch (Exception e) {
            log.error("处理分段 [{}-{}] 失败", segment.getStartId(), segment.getEndId(), e);
            return 0;
        }
    }
    
    /**
     * 处理单条记录 - 业务逻辑
     */
    private boolean processSingleRecord(TargetTable record) {
        try {
            // 这里实现具体的业务逻辑
            // 例如：数据转换、业务计算、调用外部服务等
            
            // 示例业务逻辑：模拟处理
            boolean processSuccess = mockBusinessProcess(record);
            
            if (!processSuccess) {
                // 处理失败，更新状态
                targetTableService.updateRecordStatus(record.getId(), "FAILED", "业务处理失败");
                return false;
            }
            
            // 处理成功，状态会在批量更新中统一更新
            return true;
            
        } catch (Exception e) {
            log.error("处理记录 {} 时发生异常", record.getId(), e);
            targetTableService.updateRecordStatus(record.getId(), "FAILED", e.getMessage());
            return false;
        }
    }
    
    /**
     * 模拟业务处理
     */
    private boolean mockBusinessProcess(TargetTable record) {
        // 模拟业务处理，这里替换为实际业务逻辑
        try {
            // 模拟处理时间
            Thread.sleep(1);
            
            // 模拟90%的成功率
            return Math.random() > 0.1;
        } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
            return false;
        }
    }
}